<?php
/**
 * Oolala Framework
 * 
 * @category    Oolala
 * @package     Oolala_Acl
 * @copyright   Copyright (c) 2009 Thierry Jossermoz
 * @license     New BSD License
 */
class Oolala_Acl_ResourceAcl
{
	/**
     * Map of Zend_Acl's methods and index of the resource parameter
     * 
     * @var array
     */
    protected static $_methodsMap = array(
        'allow'         => 1,
        'deny'          => 1,
        'inherits'      => 0,
        'isAllowed'     => 1,
        'removeAllow'   => 1,
        'removeDeny'    => 1
    );

    /**
     * Acl instance
     * 
     * @var Zend_Acl
     */
    protected $_acl;

    /**
     * Resource instance
     * 
     * @var Zend_Acl_Resource_Interface
     */
    protected $_resource;

    /**
     * Constructor
     * 
     * @param Zend_Acl_Resource_Interface $resource
     * @param Zend_Acl $acl
     * @return void
     */
    public function __construct(Zend_Acl_Resource_Interface $resource, Zend_Acl $acl)
    {
        $this->_acl = $acl;
        $this->_resource = $resource;
    }

    /**
     * Retrieve acl instance
     * 
     * @return Zend_Acl
     */
    public function getAcl()
    {
        return $this->_acl;
    }

    /**
     * Add the resource to the acls
     * 
     * @param null|String|Zend_Acl_Resource_Interface $parent
     * @return Oolala_Acl_ResourceAcl
     */    
    public function addSelf($parent = null)
    {
        // For backward compatibility < 1.9.1
        if (!method_exists($this->_acl, 'addResource')) {
            $this->_acl->add($this->_resource, $parent);
        } else {
            $this->_acl->addResource($this->_resource, $parent);
        }

        return $this;
    }

    /**
     * Check if the resource is registered in the Acls
     * 
     * @return boolean
     */
    public function hasSelf()
    {
        return $this->_acl->has($this->_resource);
    }
    
    /**
     * Retrieve resource
     * 
     * @return Zend_Acl_Resource_Interface
     */
    public function getSelf()
    {
        return $this->_resource;
    }
    
    /**
     * Remove resource from acls
     * 
     * @return Oolala_Acl_ResourceAcl
     */
    public function removeSelf()
    {
        $this->_acl->remove($this->_resource);
        
        return $this;
    }

    /**
     * Magic method proxying to Zend_Acl's methods with prepopulated resource parameter
     * 
     * @uses Zend_Acl::*()
     */
    public function __call($method, $args)
    {
        // if method is in the methods map array
        if (array_key_exists($method, self::$_methodsMap)) {
            // retrieve index of the resource param
            $index = self::$_methodsMap[$method];
            
            // if optional params before resource are not set, fill with null values
            if (count($args) < $index) {
                $args = array_merge($args, array_fill(count($args),  $index - count($args), null));
            }

            // insert the resource into the arguments array at the specified index
            $start  = array_slice($args, 0, $index);
            $end    = array_slice($args, $index);
            $args = array_merge($start, array($this->_resource), $end);

            // call equivalent Zend_Acl's method
            $result = call_user_func_array(array($this->_acl, $method), $args);

            if ($result instanceof Zend_Acl) {
                // return this instance for fluent interface methods
                return $this;
            }

            // return method's result otherwise            
            return $result;
        }

        // Method not supported
        throw new Zend_Acl_Exception(sprintf('Method \'%1$s\' is not accessible from the resource\'s perspective.', $method));
    }
}